/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.ext.zul;


import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.web.ext.Runnable;
import cn.easyplatform.web.ext.*;
import org.zkoss.zk.ui.Component;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treecol;
import org.zkoss.zul.Treeitem;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class TreeExt extends Tree implements ZkExt, Reloadable,
        Assignable {
    /**
     * 按指定的栏位值分组
     */
    public final static String DEFAULT = "default";
    /**
     * 具有上下关联的数据结构，且具有其它子结构
     */
    public final static String DUAL = "dual";
    /**
     * 具有上下关联的数据结构
     */
    public final static String GROUP = "group";

    /**
     * 查询语句，可以有多个栏位
     */
    private String _query;

    /**
     * 数据连接的资源id
     */
    private String _dbId;

    /**
     * 分组查询
     */
    private String _groupQuery;

    /**
     * 在显示时是否要马上执行查询
     */
    private boolean _immediate = true;

    /**
     * 表示值的栏位
     */
    private String _valueField;

    /**
     * 分组显示
     */
    private String _groupField;

    /**
     * 列表头
     */
    private String _title;

    /**
     * 每一页的笔数
     */
    private int _pageSize;

    /**
     * 排序
     */
    private String _orderBy;

    /**
     * 显示行数
     */
    private boolean _showRowNumbers;

    /**
     * 过滤表达式
     */
    private String _filter;

    /**
     * 分页组件是否显示详细信息
     */
    private boolean _pagingDetailed = true;

    /**
     * 分页组件显示模式
     */
    private String _pagingMold;

    /**
     * 分页组件显示style
     */
    private String _pagingStyle;

    /**
     * 列头可否调整大小
     */
    private boolean _sizable = true;

    /**
     * 前台逐行脚本
     */
    private String _rowScript;

    /**
     * 需要统计的栏位，可以多个，以逗分号分隔
     */
    private String _totalColumns;

    /**
     * 最后总计表达式
     */
    private String _totalScript;

    /**
     * groupQuery不为空时是否延时加载分组数据
     */
    private boolean _lazy;

    /**
     * 当pageSize大于0，fetchAll为true，表示获取所有的数据，不需要分页，使用本身的分页
     */
    private boolean _fetchAll;
    /**
     * 是否填充右边空余空间
     */
    private boolean _span;

    /**
     * 行风格
     */
    private String _rowStyle;

    /**
     * 头部风格
     */
    private String _headStyle;

    /**
     * 冻结的列数，从左到右开始
     */
    private int _frozenColumns;

    /**
     * 初始打开的层次
     */
    private int _depth;

    /**
     * 冻结头部风格
     */
    private String _frozenStyle = "background: #dfded8";

    /**
     * 当checkall为true时选择的层次，往下打开层次
     */
    private int _checkDepth;

    /**
     * 类型
     */
    private String _type = DEFAULT;
    /**
     * 双层时分层名称
     */
    private String _dualName;
    /**
     * 是否必需重新加载
     */
    private boolean _force;

    @Override
    public boolean isForce() {
        return _force;
    }

    public void setForce(boolean force) {
        this._force = force;
    }

    public String getDualName() {
        return _dualName;
    }

    public void setDualName(String dualName) {
        this._dualName = dualName;
    }

    public String getType() {
        return _type;
    }

    public void setType(String type) {
        this._type = type;
    }

    public boolean isSpan() {
        return _span;
    }

    public void setSpan(boolean span) {
        this._span = span;
    }

    public String getRowStyle() {
        return _rowStyle;
    }

    public void setRowStyle(String rowStyle) {
        this._rowStyle = rowStyle;
    }

    public String getHeadStyle() {
        return _headStyle;
    }

    public void setHeadStyle(String headStyle) {
        this._headStyle = headStyle;
    }

    public int getFrozenColumns() {
        return _frozenColumns;
    }

    public void setFrozenColumns(int frozenColumns) {
        this._frozenColumns = frozenColumns;
    }

    public int getDepth() {
        return _depth;
    }

    public void setDepth(int depth) {
        this._depth = depth;
    }

    public String getFrozenStyle() {
        return _frozenStyle;
    }

    public void setFrozenStyle(String frozenStyle) {
        this._frozenStyle = frozenStyle;
    }

    public int getCheckDepth() {
        return _checkDepth;
    }

    public void setCheckDepth(int checkDepth) {
        this._checkDepth = checkDepth;
    }

    public boolean isFetchAll() {
        return _fetchAll;
    }

    public void setFetchAll(boolean fetchAll) {
        this._fetchAll = fetchAll;
    }

    public boolean isLazy() {
        return _lazy;
    }

    public void setLazy(boolean lazy) {
        this._lazy = lazy;
    }

    public String getTotalColumns() {
        return _totalColumns;
    }

    public void setTotalColumns(String totalColumns) {
        this._totalColumns = totalColumns;
    }

    public String getTotalScript() {
        return _totalScript;
    }

    public void setTotalScript(String totalScript) {
        this._totalScript = totalScript;
    }

    public String getRowScript() {
        return _rowScript;
    }

    public void setRowScript(String rowScript) {
        this._rowScript = rowScript;
    }

    public String getGroupQuery() {
        return _groupQuery;
    }

    public void setGroupQuery(String groupQuery) {
        this._groupQuery = groupQuery;
    }

    public boolean isSizable() {
        return _sizable;
    }

    public void setSizable(boolean sizable) {
        this._sizable = sizable;
    }

    public String getPagingMold() {
        return _pagingMold;
    }

    public void setPagingMold(String pagingMold) {
        this._pagingMold = pagingMold;
    }

    public String getPagingStyle() {
        return _pagingStyle;
    }

    public void setPagingStyle(String pagingStyle) {
        this._pagingStyle = pagingStyle;
    }

    public boolean isPagingDetailed() {
        return _pagingDetailed;
    }

    public void setPagingDetailed(boolean pagingDetailed) {
        this._pagingDetailed = pagingDetailed;
    }

    public String getFilter() {
        return _filter;
    }

    public void setFilter(String filter) {
        this._filter = filter;
    }

    public int getPageSize() {
        return _pageSize;
    }

    public void setPageSize(int pageSize) {
        this._pageSize = pageSize;
        reload();
    }

    public String getOrderBy() {
        return _orderBy;
    }

    public void setOrderBy(String orderBy) {
        this._orderBy = orderBy;
    }

    public boolean isShowRowNumbers() {
        return _showRowNumbers;
    }

    public void setShowRowNumbers(boolean showRowNumbers) {
        this._showRowNumbers = showRowNumbers;
    }

    public String getValueField() {
        return _valueField;
    }

    public void setValueField(String valueField) {
        if (valueField != null)
            valueField = valueField.toUpperCase();
        this._valueField = valueField;
    }

    public String getTitle() {
        return _title;
    }

    public void setTitle(String title) {
        this._title = title;
    }

    public String getGroupField() {
        return _groupField;
    }

    public void setGroupField(String groupField) {
        if (groupField != null)
            groupField = groupField.toUpperCase();
        this._groupField = groupField;
    }

    public boolean isImmediate() {
        return _immediate;
    }

    public void setImmediate(boolean immediate) {
        this._immediate = immediate;
    }

    public String getQuery() {
        return _query;
    }

    public void setQuery(String query) {
        this._query = query;
    }

    public String getDbId() {
        return _dbId;
    }

    public void setDbId(String dbId) {
        this._dbId = dbId;
    }

    @Override
    public void reload() {
        Widget wgt = (Widget) this.getAttribute("$proxy");
        if (wgt != null)
            wgt.reload(this);
    }


    @Override
    public void setValue(Object value) {
        if (value != null) {
            if (value instanceof Object[]) {
                for (Object val : (Object[]) value) {
                    Iterator<Component> itr = queryAll("treeitem").iterator();
                    boolean search = false;
                    while (itr.hasNext()) {
                        Treeitem li = (Treeitem) itr.next();
                        Node node = li.getValue();
                        if (node.getData() != null) {
                            FieldVo[] fvs = node.getData();
                            for (FieldVo field : fvs) {
                                if (field.getName().equals(getValueField())
                                        && Lang.equals(field.getValue(), val)) {
                                    li.setSelected(true);
                                    search = true;
                                    break;
                                }
                            }
                            if (search)
                                break;
                        }
                    }
                }
            } else {
                Iterator<Component> itr = queryAll("treeitem").iterator();
                while (itr.hasNext()) {
                    Treeitem li = (Treeitem) itr.next();
                    Node node = li.getValue();
                    if (node.getData() != null) {
                        FieldVo[] fvs = node.getData();
                        for (FieldVo field : fvs) {
                            if (field.getName().equals(getValueField())
                                    && Lang.equals(field.getValue(), value)) {
                                li.setSelected(true);
                                return;
                            }
                        }
                    }
                }
            }
        }
    }


    @Override
    public Object getValue() {
        if (getSelectedCount() > 0) {
            if (isMultiple()) {
                Object[] val = new Object[getSelectedCount()];
                int index = 0;
                for (Treeitem item : getSelectedItems()) {
                    Node node = item.getValue();
                    if (node.getData() != null) {
                        FieldVo[] fvs = node.getData();
                        if (fvs != null) {
                            for (FieldVo field : fvs) {
                                if (field.getName().equals(getValueField())) {
                                    val[index++] = field.getValue();
                                    break;
                                }
                            }
                        }
                    }
                }
                return val;
            } else {
                Node node = getSelectedItem().getValue();
                FieldVo[] fvs = node.getData();
                if (fvs != null) {
                    for (FieldVo field : fvs) {
                        if (field.getName().equals(getValueField()))
                            return field.getValue();
                    }
                }
            }
        }
        return null;
    }

    public Object getData(String name, boolean sel) {
        if (Strings.isBlank(name))
            return null;
        if (sel) {
            if (getSelectedItem() == null)
                return null;
            Treeitem li = getSelectedItem();
            Node node = li.getValue();
            if (node.getData() != null) {
                FieldVo[] fvs = node.getData();
                for (FieldVo field : fvs) {
                    if (field.getName().equalsIgnoreCase(name))
                        return field.getValue();
                }
            }
            return null;
        } else {
            List<Object> data = new ArrayList<Object>();
            for (Treeitem li : getItems()) {
                Node node = li.getValue();
                FieldVo[] fvs = node.getData();
                if (fvs != null) {
                    for (FieldVo fv : fvs) {
                        if (fv.getName().equals(name)) {
                            data.add(fv.getValue());
                            break;
                        }
                    }
                }
            }
            Object[] objs = new Object[data.size()];
            data.toArray(objs);
            return objs;
        }
    }

    public Object getSelectedData() {
        if (getSelectedCount() > 0) {
            if (isMultiple()) {
                List<Object[]> data = new ArrayList<>();
                for (Treeitem ti : getSelectedItems()) {
                    Node node = ti.getValue();
                    FieldVo[] fvs = node.getData();
                    if (fvs != null) {
                        Object[] values = new Object[fvs.length];
                        for (int i = 0; i < values.length; i++)
                            values[i] = fvs[i].getValue();
                        data.add(values);
                    }
                }
                Object[] tmp = new Object[data.size()];
                data.toArray(tmp);
                return tmp;
            } else {
                Treeitem li = getSelectedItem();
                Node node = li.getValue();
                FieldVo[] fvs = node.getData();
                if (fvs != null) {
                    Object[] data = new Object[fvs.length];
                    for (int i = 0; i < data.length; i++)
                        data[i] = fvs[i].getValue();
                    return data;
                }
            }
        }
        return null;
    }

    public Object[] getData() {
        List<Object> data = new ArrayList<Object>();
        for (Treeitem li : getItems()) {
            Node node = li.getValue();
            FieldVo[] fvs = node.getData();
            if (fvs != null) {
                Object[] values = new Object[fvs.length];
                for (int i = 0; i < values.length; i++)
                    values[i] = fvs[i].getValue();
                data.add(values);
            }
        }
        Object[] objs = new Object[data.size()];
        data.toArray(objs);
        return objs;
    }

    public List<Object[]> getData(boolean sel) {
        List<Object[]> data = new ArrayList<Object[]>();
        if (sel) {
            if (getSelectedCount() > 0) {
                for (Treeitem li : getSelectedItems()) {
                    Node node = li.getValue();
                    FieldVo[] fvs = node.getData();
                    if (fvs != null)
                        data.add(fvs);
                }
            }
        } else {
            for (Treeitem li : getItems()) {
                Node node = li.getValue();
                FieldVo[] fvs = node.getData();
                if (fvs != null)
                    data.add(fvs);
            }
        }
        return data;
    }

    public FieldVo[] getRawValue() {
        Treeitem li = getSelectedItem();
        if (li != null) {
            Node node = li.getValue();
            return node.getData();
        }
        return null;
    }

    public void export(String type) {
        Exportable export = (Exportable) getAttribute("$proxy");
        export.export(type);
    }


    public Component setColumnName(int index, String name) {
        Treecol header = getHeader(index);
        header.setLabel(name);
        return header;
    }

    public Component setColumnVisible(int index, boolean visible) {
        Treecol header = getHeader(index);
        header.setVisible(visible);
        return header;
    }

    public Treecol getHeader(int index) {
        return (Treecol) getTreecols().getChildren().get(index);
    }

    /**
     * 执行指定的功能
     *
     * @param args
     */
    public void go(Object... args) {
        Runnable proxy = (Runnable) getAttribute("$proxy");
        proxy.go(args);
    }
}
